#include "ComponentEatPickableBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentCreatureSounds.h"
#include "Components/ComponentCreatureModel.h"

#include "ComponentPathfinding.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemPickables.h"
#include "Subsystems/SubsystemTimeOfDay.h"

#include "GameRandom.hpp"
#include "Engine/MathUtils.hpp"
#include "Log.h"
#include "Managers/BlocksManager.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

#include <iostream>

namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentEatPickableBehavior::StateMachine::StateMachine(ComponentEatPickableBehavior *eatPickableBehavior) : m_eatPickable(eatPickableBehavior), EasyMachine(MachineState::Inactive)
    {
        enter();
    }

    void ComponentEatPickableBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                m_eatPickable->m_importanceLevel = 0.0f;
                m_eatPickable->m_pickable = nullptr;
            }
            break;
        case MachineState::Move:
            MACHINE_DELEGATE()
            {
                if (m_eatPickable->m_pickable != nullptr)
                {
                    float speed = (m_eatPickable->m_satiation == 0.0f) ? GameSingleton::gameRandom->getFloat(0.5f, 0.7f) : 0.5f;
                    int32_t maxPathfindingPositions = (m_eatPickable->m_satiation == 0.0f) ? 1000 : 500;

                    Vector3 position;
                    m_eatPickable->componentCreature->componentBody->getPosition(position);

                    float num2 = Vector3::Distance(m_eatPickable->componentCreature->componentCreatureModel->getEyePosition(), position);
                    m_eatPickable->componentPathfinding->setDestination(m_eatPickable->m_pickable->position, speed, 1.0f + num2, maxPathfindingPositions, true, false, true, nullptr);
                    if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.66f)
                    {
                        // 播放声音
                    }
                }
            }
            break;
        case MachineState::PickableMoved:
            break;
        case MachineState::Eat:
            MACHINE_DELEGATE()
            {
                m_eatPickable->setIsFeed(true);
                m_eatPickable->m_eatTime = GameSingleton::gameRandom->getFloat(4.0f, 5.0f);
                m_eatPickable->m_blockedTime = 0;
            }
            break;
        }
    }

    void ComponentEatPickableBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                if (m_eatPickable->m_satiation < 1.0f)
                {
                    if (m_eatPickable->m_pickable == nullptr)
                    {
                        if (m_eatPickable->subsystemTimeOfDay->getGameTime() > m_eatPickable->m_nextFindPickableTime)
                        {
                            Vector3 position;
                            m_eatPickable->componentCreature->componentBody->getPosition(position);

                            m_eatPickable->m_nextFindPickableTime = m_eatPickable->subsystemTimeOfDay->getGameTime() + GameSingleton::gameRandom->getFloat(2.0f, 4.0f);
                            m_eatPickable->m_pickable = m_eatPickable->findPickable(position);
                        }
                    }
                    else
                    {
                        m_eatPickable->m_importanceLevel = GameSingleton::gameRandom->getFloat(5.0f, 10.0f);
                    }
                }
                if (m_eatPickable->isActive())
                {
                    changeTo(MachineState::Move);
                    m_eatPickable->m_blockedCount = 0;
                }
            }
            break;
        case MachineState::Move:
            MACHINE_DELEGATE()
            {
                if (m_eatPickable->isActive() == false)
                {
                    changeTo(MachineState::Inactive);
                }
                else if (m_eatPickable->m_pickable == nullptr)
                {
                    m_eatPickable->m_importanceLevel = 0.0f;
                }
                else if (m_eatPickable->componentPathfinding->isStuck)
                {
                    m_eatPickable->m_importanceLevel = 0.0f;
                    m_eatPickable->m_satiation += 0.75f;
                }
                else if (m_eatPickable->componentPathfinding->destination == FastVector3::Zero)
                {
                    changeTo(MachineState::Eat);
                }
                else if (Vector3::DistanceSquared(m_eatPickable->componentPathfinding->destination, m_eatPickable->m_pickable->position) > 0.0625f)
                {
                    changeTo(MachineState::PickableMoved);
                }
                if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.1f * dt)
                {
                    // 播放声音
                }
                if (m_eatPickable->m_pickable != nullptr)
                {
                    m_eatPickable->componentCreature->componentCreatureModel->lookAtOrder = std::make_shared<Vector3>(m_eatPickable->m_pickable->position);
                }
                else
                {
                    m_eatPickable->componentCreature->componentCreatureModel->lookRandomOrder = true;
                }
            }
            break;
        case MachineState::PickableMoved:
            MACHINE_DELEGATE()
            {
                if (m_eatPickable->m_pickable != nullptr)
                {
                    m_eatPickable->componentCreature->componentCreatureModel->lookAtOrder = std::make_shared<Vector3>(m_eatPickable->m_pickable->position);
                }
                if (m_eatPickable->subsystemTimeOfDay->periodicGameTimeEvent(0.25, 0))
                {
                    changeTo(MachineState::Move);
                }
            }
            break;
        case MachineState::Eat:
            MACHINE_DELEGATE()
            {
                if (m_eatPickable->isActive() == false)
                {
                    changeTo(MachineState::Inactive);
                }
                if (m_eatPickable->m_pickable == nullptr)
                {
                    m_eatPickable->m_importanceLevel = 0.0f;
                }
                else
                {
                    ComponentCreature *creature = m_eatPickable->componentCreature;
                    Vector3 eyePosition = creature->componentCreatureModel->getEyePosition();

                    Vector3 position;
                    creature->componentBody->getPosition(position);

                    if (Vector3::DistanceSquared(Vector3(eyePosition.posX, position.posY, eyePosition.posZ), m_eatPickable->m_pickable->position) < 0.64)
                    {
                        m_eatPickable->m_eatTime -= dt;
                        m_eatPickable->m_blockedTime = 0.0f;
                        if (m_eatPickable->m_eatTime < 0.0f)
                        {
                            m_eatPickable->m_satiation += 1.0f;
                            m_eatPickable->m_pickable->count = MathUtils::Max<uint32_t>(m_eatPickable->m_pickable->count - 1, 0);
                            if(m_eatPickable->m_pickable->count == 0)
                            {
                                m_eatPickable->m_pickable->toRemove == true;
                                m_eatPickable->m_importanceLevel = 0.0f;
                            }
                            else if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.5f)
                            {
                                m_eatPickable->m_importanceLevel = 0.0f;
                            }
                        }
                    }
                    else
                    {
                        float num = Vector3::Distance(eyePosition, position);
                        m_eatPickable->componentPathfinding->setDestination(m_eatPickable->m_pickable->position, 0.3f, 0.5f + num, 0, false, true, false, nullptr);
                        m_eatPickable->m_blockedTime += dt;
                    }
                    if (m_eatPickable->m_blockedTime > 3.0f)
                    {
                        m_eatPickable->m_blockedCount++;
                        if (m_eatPickable->m_blockedCount >= 3)
                        {
                            m_eatPickable->m_importanceLevel = 0.0f;
                            m_eatPickable->m_satiation += 0.75f;
                        }
                        else
                        {
                            changeTo(MachineState::Move);
                        }
                    }
                }
            }
            break;
        }
    }

    void ComponentEatPickableBehavior::StateMachine::leave()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
            }
            break;
        case MachineState::Move:
            MACHINE_DELEGATE()
            {
            }
            break;
        case MachineState::PickableMoved:
            MACHINE_DELEGATE()
            {
            }
            break;
        case MachineState::Eat:
            MACHINE_DELEGATE()
            {
                m_eatPickable->setIsFeed(false);
            }
            break;
        }
    }

    // **************************************



    ComponentEatPickableBehavior::~ComponentEatPickableBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentEatPickableBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    bool ComponentEatPickableBehavior::getIsFeed()
    {
        return m_feed;
    }
    void ComponentEatPickableBehavior::setIsFeed(bool feed)
    {
        m_feed = feed;
    }

    Pickable *ComponentEatPickableBehavior::findPickable(const Vector3 &position)
    {
        if (subsystemTimeOfDay->getGameTime() > m_nextPickablesUpdateTime)
        {
            m_nextPickablesUpdateTime = subsystemTimeOfDay->getGameTime() + GameSingleton::gameRandom->getFloat(2.0f, 4.0f);
            m_pickablSetLock.lock();
            m_pickableSet.clear();
            m_pickablSetLock.unlock();

            std::vector<Pickable *> pickableVec;
            subsystemPickables->getNowNotRemovePickables(pickableVec);
            for (Pickable *pickable : pickableVec)
            {
                tryAddPickable(pickable);
            }

            m_pickablSetLock.lock();
            if (m_pickable != nullptr &&  m_pickableSet.find(m_pickable) == m_pickableSet.end())
            {
                m_pickable = nullptr;
            }
            m_pickablSetLock.unlock();
        }

        std::lock_guard<SpinLock> lk(m_pickablSetLock);
        for (Pickable * pickable : m_pickableSet)
        {
            float num = Vector3::DistanceSquared(position, pickable->position);
            if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) > num / 256.0f)
            {
                return pickable;
            }
        }
        return nullptr;
    }

    bool ComponentEatPickableBehavior::tryAddPickable(Pickable *pickable)
    {
        Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(pickable->value)];
        Vector3 position;
        componentCreature->componentBody->getPosition(position);

        if (m_foodFactors[(uint8_t)block->FoodType] > 0.0f && Vector3::DistanceSquared(pickable->position, position) < 256.0f)
        {
            m_pickablSetLock.lock();
            m_pickableSet.insert(pickable);
            m_pickablSetLock.unlock();
            return true;
        }
        return false;
    }

    void ComponentEatPickableBehavior::update(float dt)
    {
        if (m_pickable != nullptr && m_pickable->toRemove == true)
        {
            m_pickable = nullptr;
        }

        if (m_satiation > 0.0f)
        {
            m_satiation = MathUtils::Max(m_satiation - 0.01f * dt, 0.0f);
        }
        m_stateMachine->update(dt);
    }

    static const std::string componentName = "EatPickableBehavior";
    const std::string &ComponentEatPickableBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentEatPickableBehavior::GetName()
    {
        return componentName;
    }
    void ComponentEatPickableBehavior::load(const nlohmann::json &jsonData)
    {
        componentCreature    = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding *>(entity);

        subsystemPickables = GameSingleton::singletonPtr->getSingleton<SubsystemPickables>();
        subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        
        m_stateMachine = new StateMachine(this);

        const nlohmann::json &foodFactorsJson = jsonData["FoodFactors"];
        m_foodFactors[(uint8_t)BlockFoodType::Meat] = foodFactorsJson["Meat"].get<float>();
        m_foodFactors[(uint8_t)BlockFoodType::Fish] = foodFactorsJson["Fish"].get<float>();
        m_foodFactors[(uint8_t)BlockFoodType::Fruit] = foodFactorsJson["Fruit"].get<float>();
        m_foodFactors[(uint8_t)BlockFoodType::Grass] = foodFactorsJson["Grass"].get<float>();
        m_foodFactors[(uint8_t)BlockFoodType::Bread] = foodFactorsJson["Bread"].get<float>();
    }

}
